package top.codedance.iotp.converter.service.slave;

import top.codedance.iotp.converter.ConverterApplication;
import top.codedance.iotp.converter.bean.SlaveConnectParam;
import top.codedance.iotp.converter.service.slave.holder.AsciiDataHolder;
import top.codedance.iotp.converter.service.slave.listenner.AsciiListener;
import com.intelligt.modbus.jlibmodbus.Modbus;
import com.intelligt.modbus.jlibmodbus.data.ModbusCoils;
import com.intelligt.modbus.jlibmodbus.data.ModbusHoldingRegisters;
import com.intelligt.modbus.jlibmodbus.serial.*;
import com.intelligt.modbus.jlibmodbus.slave.ModbusSlave;
import com.intelligt.modbus.jlibmodbus.slave.ModbusSlaveFactory;
import com.intelligt.modbus.jlibmodbus.utils.SerialPortInfo;
import org.apache.log4j.Logger;

import java.util.Observable;

public class AsciiSlave extends ModbusAbstractAdapter {
    private Logger logger = ConverterApplication.getLogger(AsciiSlave.class);
    private ModbusSlave slave = null;
    private SerialParameters sp = null;

    public AsciiSlave(SlaveConnectParam scp) {
        super(scp);
        Modbus.setLogLevel(ConverterApplication.getModbusLogLevel());
    }

    @Override
    public void init() {
        connect();
        long time = System.currentTimeMillis();
        while (true) {
            if (!isOpen()) {
                try {
                    connect();
                }catch (Exception ex){
                    logger.info(ex.getMessage());
                }
                logger.info("Ascii slave is reboot!");
            }else{
                if(System.currentTimeMillis() - time > 1000 * 3600 * 2){
                    destory();
                    try {
                        connect();
                    }catch (Exception ex){
                        logger.info(ex.getMessage());
                    }
                    time = System.currentTimeMillis();
                }
            }
            try {
                Thread.sleep(3000);
            } catch (Exception ex) { }
        }
    }

    @Override
    public void connect() {
        try {
            sp = new SerialParameters();
            sp.setParity(SerialPort.Parity.getParity(param.getParity()));
            sp.setStopBits(param.getStopBits());
            sp.setDataBits(param.getDataBits());
            sp.setBaudRate(SerialPort.BaudRate.getBaudRate(param.getBaudRate()));
            sp.setDevice(param.getComName());
            SerialUtils.setSerialPortFactory(new SerialPortFactoryPJC());
            slave = ModbusSlaveFactory.createModbusSlaveASCII(sp);
            slave.setServerAddress(param.getId());
            slave.setBroadcastEnabled(true);
            slave.setReadTimeout(10000);
            slave.addObserver((Observable o, Object arg) -> {
                SerialPortInfo spi = (SerialPortInfo)arg;
                AsciiListener asciiListener = new AsciiListener();
                if(spi.isOpened()) {
                    slave.addListener(asciiListener);
                }else{
                    slave.removeListener(asciiListener);
                }
            });
            AsciiDataHolder asciiDataHolder = new AsciiDataHolder();
            if(ConverterApplication.masterDataParams.containsKey(REGISTER_NAME)) {
                Integer[] registersAddress = ConverterApplication.masterDataParams.get(REGISTER_NAME);
                if (registersAddress.length > 0) {
                    ModbusHoldingRegisters holdingRegisters = new ModbusHoldingRegisters(registersAddress.length);
                    asciiDataHolder.setHoldingRegisters(holdingRegisters);
                }
            }
            if(ConverterApplication.masterDataParams.containsKey(COIL_NAME)) {
                Integer[] coilsAddress = ConverterApplication.masterDataParams.get(COIL_NAME);
                if (coilsAddress.length > 0) {
                    ModbusCoils coils = new ModbusCoils(coilsAddress.length);
                    asciiDataHolder.setCoils(coils);
                }
            }
            slave.setDataHolder(asciiDataHolder);
            Modbus.setAutoIncrementTransactionId(true);
            slave.listen();
            if (slave.isListening()) {
                logger.info("Ascii slave running!");
                Runtime.getRuntime().addShutdownHook(new Thread(() -> {
                    synchronized (slave) {
                        slave.notifyAll();
                    }
                }));

                synchronized (slave) {
                    slave.wait();
                }
                slave.shutdown();
            }else{
                logger.info("Ascii slave dead!");
            }
        }catch (Exception e){
            logger.debug(e.getMessage());
        }
    }

    @Override
    public boolean isOpen() {
        return slave.isListening();
    }

    @Override
    public void destory() {
        if(slave != null) {
            try {
                slave.shutdown();
            } catch (Exception e) {
                logger.debug(e.getMessage());
            }
        }
    }
}
